package cmd

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

func SyncExecute(c exec.Cmd) ([]string, error) {
	cwd, _ := os.Getwd()
	fmt.Println("time === ", time.Now())
	fmt.Fprintf(os.Stdout, "%s %s\n", path.Join(cwd, c.Dir), strings.Join(c.Args, " "))

	var stdout, stderr []byte
	var errStdout, errStderr error
	stouter, _ := c.StdoutPipe()
	steeper, _ := c.StderrPipe()
	//stein, _ := c.StdinPipe()
	//defer func() {
	//	if stein != nil {
	//		_ = stein.Close()
	//	}
	//}()
	err := c.Start()
	if err != nil {
		fmt.Errorf("cmd.Start() failed with '%s'", err.Error())
		return nil, err
	}

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		stdout, errStdout = copyAndCapture(os.Stdout, stouter)
		wg.Done()
		fmt.Println("std out down")
	}()

	go func() {
		stderr, errStderr = copyAndCapture(os.Stderr, steeper)
		wg.Done()
		fmt.Println("std err down")
	}()
	//go func() {
	//	time.Sleep(60 * time.Second) // sleep是为了把所有这个时间的回放直播流拉回来
	//	fmt.Println("ready to quit")
	//	_, err := io.WriteString(stein, "q\n")
	//	_, err = io.WriteString(stein, "quit\n")
	//	fmt.Println("err == ", err)
	//}()

	err = c.Wait()
	fmt.Println("cmd wait finished")
	if err != nil {
		if c.ProcessState.ExitCode() != 1 {
			fmt.Errorf("cmd.Run() failed with %s", err.Error())
			return nil, err
		}
	}

	if errStdout != nil {
		fmt.Errorf("failed to capture stdout")
		return nil, errStdout
	}

	if errStderr != nil {
		fmt.Errorf("failed to capture stderr")
		return nil, errStderr
	}
	wg.Wait()

	return []string{string(stdout), string(stderr)}, nil
}

func AsyncExecute(c exec.Cmd) error {
	cwd, _ := os.Getwd()
	fmt.Fprintf(os.Stdout, "%s %s\n", path.Join(cwd, c.Dir), strings.Join(c.Args, " "))
	{
		stdoutPipe, err := c.StdoutPipe()
		if err != nil {
			return err
		}
		go scanAndStdout(bufio.NewScanner(stdoutPipe))
	}
	{
		stderrPipe, err := c.StderrPipe()
		if err != nil {
			return err
		}
		go scanAndStderr(c, bufio.NewScanner(stderrPipe))
	}

	if err := c.Run(); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		return err
	}
	return nil
}

func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
	var out []byte
	buf := make([]byte, 1024)
	for {
		n, err := r.Read(buf[:])
		if n > 0 {
			d := buf[:n]
			out = append(out, d...)
			_, err := w.Write(d)
			if err != nil {
				return out, err
			}
		}
		if err != nil {
			if err == io.EOF {
				err = nil
			}
			return out, err
		}
	}
}

func scanAndStdout(scanner *bufio.Scanner) {
	for scanner.Scan() {
		fmt.Fprintln(os.Stdout, scanner.Text())
	}
}

func scanAndStderr(c exec.Cmd, scanner *bufio.Scanner) {
	for scanner.Scan() {
		regStr := `More than (\d+) frames duplicated`
		re, _ := regexp.Compile(regStr)
		dup := re.FindStringSubmatch(scanner.Text())
		if len(dup) >= 2 {
			ph, _ := strconv.ParseInt(dup[1], 10, 64)
			if ph >= 1000 {
				fmt.Println("Error：", scanner.Text())
				_ = c.Process.Kill()
				os.Stderr.WriteString(dup[0] + "  ")
				return
			}
		}
		fmt.Fprintln(os.Stderr, scanner.Text())
	}
}
